package com.nega.commons.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * Util - 时间工具类
 *
 * @author Garc
 */
public class DateUtils {
    private static final Logger LOG = LoggerFactory.getLogger(DateUtils.class);

    private DateUtils() {

    }

    private static Date defaultDate;

    private static String[] optionDateFormats = new String[] { "yyyy-MM-dd HH:mm:ss.S a", "yyyy-MM-dd HH:mm:ssz", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ssa" };

    private static final FastDateFormat DATE_TIME_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");

    /**
     * 一秒毫秒数
     */
    private static final long SECOND_MILLISECONDS = getConstantValue(1000L);
    /**
     * 一小时毫秒数
     */
    private static final long HOUR_SECONDS        = getConstantValue(3600L);
    /**
     * 一天小时
     */
    private static final long DAY_HOURS           = getConstantValue(24L);
    /**
     * 一年天数
     */
    private static final long YEAR_DAYS           = getConstantValue(365L);
    /**
     * 一小时60分钟
     */
    private static final int  HOUR_MINUTES        = getConstantValue(60);
    /**
     * 一年毫秒数
     */
    private static final long YEAR_TIMEMILLIS     = getConstantValue(SECOND_MILLISECONDS * HOUR_SECONDS * DAY_HOURS * YEAR_DAYS);
    /**
     * 一天毫秒数
     */
    private static final long DAY_TIMEMILLIS      = getConstantValue(SECOND_MILLISECONDS * HOUR_SECONDS * DAY_HOURS);
    /**
     * 一小时毫秒数
     */
    private static final long HOUR_TIMEMILLIS     = getConstantValue(SECOND_MILLISECONDS * HOUR_SECONDS);
    /**
     * 一分钟毫秒数
     */
    private static final long MINUTE_TIMEMILLIS   = getConstantValue(SECOND_MILLISECONDS * HOUR_MINUTES);

    private static final long   DATE_SIZE            = getConstantValue(-1);
    public static final  String YYYY_MM_DD           = getConstantValue("yyyy-MM-dd");
    public static final  String YYYY_MM_DD_HH_MM_SS  = getConstantValue("yyyy-MM-dd HH:mm:ss");
    public static final  String YYYYMMDDHHMMSS       = getConstantValue("yyyyMMddHHmmss");
    private static final int    BIGDECIMAL_DIGITAL_2 = getConstantValue(2);

    /**
     * 值大于0小于10时，左侧补零
     */
    private static final int TIME_LPAD_ZERO_MAX_VALUE = getConstantValue(10);


    /**
     * 传入值，返回一个值，通过这种方式获取常量，可以避免增量发布时，引用常量的地方指向错误
     *
     * @param value 传入的作为常量的值
     * @param <T>   类型参数
     * @return 常量值
     */
    public static <T> T getConstantValue(T value) {
        return value;
    }

    /**
     * 格式化日期输出，如果格式为空，则默认为yyyy-MM-dd
     *
     * @param date
     * @param dateFormat
     * @return
     */
    public static String dateToStringFormat(Date date, String dateFormat) {
        if (date == null) {
            return "";
        }

        if (StringUtils.isBlank(dateFormat)) {
            dateFormat = YYYY_MM_DD;
        }

        try {
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
            return sdf.format(date);
        } catch (Exception e) {
            LOG.info("INFO", e);
            return null;
        }
    }

    /**
     * 给定指定模式，指定日期字符串
     *
     * @param format 模式如(yyyyMMddHHmmss...)
     * @param date   日期
     * @return 解释后返回的日期对象
     */
    public static Date string2Date(String date, String format) {
        if (StringUtils.isBlank(date)) {
            return null;
        }
        if (StringUtils.isBlank(format)) {
            format = YYYY_MM_DD_HH_MM_SS;
        }
        Date parseDate = null;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        try {
            parseDate = simpleDateFormat.parse(date);
        } catch (ParseException e) {
            LOG.error(e.getMessage(), e);
        }
        return parseDate;
    }

    /**
     * get the year of a date
     *
     * @param date Date the date which the date get from
     * @return int the year of the date
     */
    public static int getYear(Date date) {
        Calendar calendar = createCalendar();
        setCalTime(date, calendar);
        return calendar.get(Calendar.YEAR);
    }

    /**
     * get the month of a date
     *
     * @param date Date the date which the month get from
     * @return int the month of the date
     */
    public static int getMonth(Date date) {
        Calendar calendar = createCalendar();
        setCalTime(date, calendar);
        return calendar.get(Calendar.MONTH) + 1;
    }

    /**
     * get the day of a date
     *
     * @param date Date the date which the day get from
     * @return int the day of the date
     */
    public static int getDay(Date date) {
        Calendar calendar = createCalendar();
        setCalTime(date, calendar);
        return calendar.get(Calendar.DAY_OF_MONTH);
    }

    private static Calendar createCalendar() {
        Calendar result;
        result = Calendar.getInstance();
        return result;
    }

    private static void setCalTime(Date oldDate, Calendar cal) {
        cal.setTime(oldDate);
    }

    /**
     * Sets the years field to a date returning a new object. The original date
     * object is unchanged.
     *
     * @param date   the date, not null
     * @param amount the amount to set
     * @return a new Date object set with the specified value
     */
    public static Date setYears(Date date, int amount) {
        return set(date, Calendar.YEAR, amount);
    }

    /**
     * Sets the months field to a date returning a new object. The original date
     * object is unchanged.
     *
     * @param date   the date, not null
     * @param amount the amount to set
     * @return a new Date object set with the specified value
     */
    public static Date setMonths(Date date, int amount) {
        return set(date, Calendar.MONTH, amount);
    }

    /**
     * Sets the day of month field to a date returning a new object. The
     * original date object is unchanged.
     *
     * @param date   the date, not null
     * @param amount the amount to set
     * @return a new Date object set with the specified value
     */
    public static Date setDays(Date date, int amount) {
        return set(date, Calendar.DAY_OF_MONTH, amount);
    }

    private static Date set(Date date, int calendarField, int amount) {
        if (date == null) {
            return date;
        }
        Calendar c = Calendar.getInstance();
        c.setLenient(false);
        c.setTime(date);
        c.set(calendarField, amount);
        return c.getTime();
    }

    /**
     * 将日期用指定的日期格式转换成字符串
     *
     * @param date   需要转换的日期
     * @param format 指定的格式
     * @return 转换后的字符串
     */
    public static String date2String(Date date, String format) {
        if (date == null) {
            return null;
        } else {
            if (StringUtils.isEmpty(format)) {
                format = YYYY_MM_DD_HH_MM_SS;
            }
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
            return simpleDateFormat.format(date);
        }
    }

    /**
     * convert a string to a date according to the indicated format.
     *
     * @param sDate  String the string to be transferred
     * @param format String the indicated format
     * @return Date the transferred date
     */
    public static Date toDate(String sDate, String format) {
        if (StringUtils.isEmpty(sDate)) {
            return null;
        }
        return parse(sDate, format);
    }

    /**
     * convert a string to a date according to the indicated format.
     *
     * @param sDate String the string to be transferred
     * @return Date the transferred date
     */
    public static Date toDate(String sDate) {
        return toDate(sDate, YYYY_MM_DD);
    }

    private static Date parse(String date, String defaultFormat) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(defaultFormat);
        Date resultDate = null;
        try {
            resultDate = simpleDateFormat.parse(date);
        } catch (ParseException e) {
            for (int i = 0; i < optionDateFormats.length; i++) {
                try {
                    SimpleDateFormat format = new SimpleDateFormat(optionDateFormats[i]);
                    resultDate = format.parse(date);
                } catch (ParseException e2) {
                    LOG.error(e2.getMessage(), e2);
                }
            }
        }
        return resultDate;
    }

    /**
     * 设置系统日期 格式yyyy-MM-dd
     *
     * @return Date
     */
    public static Date getSysDate() {
        if (defaultDate != null) {
            return defaultDate;
        } else {
            // IFoundationDao foundationDao = (IFoundationDao)
            // SpringBeanUtils.getBean("foundationDao");
            // Date result = foundationDao.getDBDate();
            // 处于性能考虑，采用new Date()的方式获取时间，生产上应确保应用服务器和数据库服务器时间同步
            // IClock clock = SpringBeanUtils.getBean(IClock.class);
            // return clock.getTime();

            return new Date();
        }
    }

    public static void setDefaultDate(Date defaultDate) {
        DateUtils.defaultDate = defaultDate;
    }

    /**
     * 获取两个日期之前相差的年数据
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 两个日期之间相差的年数，四舍五入
     */
    public static double getYearSize(Date startDate, Date endDate) {
        if (startDate == null || endDate == null) {
            return Double.valueOf(DATE_SIZE);
        }
        long timMillis = Math.abs(startDate.getTime() - endDate.getTime());
        return BigDecimal.valueOf(timMillis).divide(BigDecimal.valueOf(YEAR_TIMEMILLIS), BIGDECIMAL_DIGITAL_2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 获取两个日期之前相差的天数据
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 相差的天数，四舍五入
     */
    public static double getDaySize(Date startDate, Date endDate) {
        if (startDate == null || endDate == null) {
            return (double) DATE_SIZE;
        }
        long timMillis = endDate.getTime() - startDate.getTime();
        return BigDecimal.valueOf(timMillis).divide(BigDecimal.valueOf(DAY_TIMEMILLIS), BIGDECIMAL_DIGITAL_2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 获取两个日期之前相差的小时数据
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 相差的小时数，四舍五入
     */
    public static double getHourSize(Date startDate, Date endDate) {
        if (startDate == null || endDate == null) {
            return (double) DATE_SIZE;
        }
        long timMillis = Math.abs(startDate.getTime() - endDate.getTime());
        return BigDecimal.valueOf(timMillis).divide(BigDecimal.valueOf(HOUR_TIMEMILLIS), BIGDECIMAL_DIGITAL_2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 获取两个时间相差分钟
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return
     */
    public static double getMinuteSize(Date startDate, Date endDate) {
        if (startDate == null || endDate == null) {
            return (double) DATE_SIZE;
        }
        long timMillis = Math.abs(startDate.getTime() - endDate.getTime());
        return BigDecimal.valueOf(timMillis % DAY_TIMEMILLIS % HOUR_TIMEMILLIS).divide(BigDecimal.valueOf(MINUTE_TIMEMILLIS), BIGDECIMAL_DIGITAL_2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 获取两个时间相差分钟
     *
     * @param startMillis 开始时间毫秒数
     * @param endMillis   结束时间毫秒数
     * @return
     */
    public static double getMinuteSize(Long startMillis, Long endMillis) {
        if (startMillis == null || endMillis == null) {
            return (double) DATE_SIZE;
        }
        return getMinuteSize(new Date(startMillis), new Date(endMillis));
    }

    /**
     * 获取两个日期相差的秒数
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 相差的秒数，四舍五入
     */
    public static long getSecondSize(Date startDate, Date endDate) {
        if (startDate == null) {
            return DATE_SIZE;
        }
        if (endDate == null) {
            endDate = DateUtils.getSysDate();
        }
        long millis = endDate.getTime() - startDate.getTime();
        return millis / SECOND_MILLISECONDS;
    }

    /**
     * 通过计算出来的剩余时间（含有小数），将其转化为hh:mm格式的剩余时间 例如：1.5小时 --> 01:30
     *
     * @param hour 剩余时间（hour为单位，可能为负值）
     * @return 格式化后的剩余时间
     */
    public static String getTimeInHHMMFormat(BigDecimal hour) {
        int firstIndex = 1;
        String hourString = hour.toString();

        if (hour.compareTo(BigDecimal.ZERO) >= 0) {
            firstIndex = 0;
        }
        if (!hourString.contains(".")) {
            String integerPartString = hourString.substring(firstIndex);
            return (integerPartString.length() == 1 ? "0" + integerPartString : integerPartString) + ":00";
        } else {
            String integerPartString = hourString.substring(firstIndex, hourString.indexOf("."));
            String decimalPart = "0" + hourString.substring(hourString.indexOf("."));
            int decimalPartInt = ((int) Math.ceil(Double.valueOf(decimalPart) * HOUR_MINUTES));
            if (decimalPartInt == HOUR_MINUTES) {
                integerPartString = (Integer.parseInt(integerPartString) + 1) + "";
                decimalPartInt = 0;
            }
            return ((integerPartString.length() == 1 ? "0" + integerPartString : integerPartString) + ":" + (decimalPartInt < TIME_LPAD_ZERO_MAX_VALUE ? "0" + decimalPartInt : decimalPartInt));
        }
    }

    /**
     * 获取当前时间
     *
     * @return currentTime
     */
    public static Date now() {
        return Calendar.getInstance().getTime();
    }

    public static Date long2Date(Long date, String format) {
        if (StringUtils.isEmpty(format)) {
            format = "yyyy-MM-dd HH:mm:ss";
        }
        Date time = null;
        if (date != null) {
            time = new Date(date);
        }
        return time;
    }

    /**
     * 将时间毫秒转换String格式
     *
     * @param time   时间
     * @param format 格式化
     * @return
     */
    public static String long2Strng(String time, String format) {
        if (StringUtils.isEmpty(format)) {
            format = "yyyy-MM-dd HH:mm:ss";
        }

        if (time != null) {
            try {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
                Long timeMillis = Long.parseLong(time);
                return simpleDateFormat.format(timeMillis);
            } catch (NumberFormatException e) {
                LOG.debug("时间转换异常:{}", e);
            }
        }
        return null;
    }

    public static String long2Strng(String time) {
        return long2Strng(time, null);
    }

    public static String long2Strng(Long time) {
        return long2Strng(time, null);
    }

    /**
     * 将时间毫秒转换String格式
     *
     * @param time   时间
     * @param format 格式化
     * @return
     */
    public static String long2Strng(Long time, String format) {
        if (StringUtils.isEmpty(format)) {
            format = "yyyy-MM-dd HH:mm:ss";
        }

        if (time != null) {
            try {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
                return simpleDateFormat.format(time);
            } catch (NumberFormatException e) {
                LOG.debug("时间转换异常:{}", e);
            }
        }
        return null;
    }

    /**
     * 得到当前时间
     *
     * @return
     */
    public static Date getCurrentDate() {
        return new Date();
    }

    /**
     * 比较两个时间的间隔，单位为毫秒
     *
     * @param endDate
     * @param startDate
     * @return
     */
    public static int diffTwoDateWithTime(Date endDate, Date startDate) {
        if (startDate == null || endDate == null) {
            return -1;
        }
        int diff = (int) (endDate.getTime() - startDate.getTime());

        return diff;
    }

    public static int getTimeonDay() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(5, calendar.get(5) + 1);
        calendar.set(11, 0);
        calendar.set(12, 0);
        calendar.set(13, 0);
        return (int) (calendar.getTime().getTime() / 1000L - (new Date()).getTime() / 1000L);
    }

    public static int getTimeonMonth() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(2, calendar.get(2) + 1);
        calendar.set(5, calendar.getMinimum(5));
        calendar.set(11, 0);
        calendar.set(12, 0);
        calendar.set(13, 0);
        return (int) (calendar.getTime().getTime() / 1000L - (new Date()).getTime() / 1000L);
    }

    /**
     * 获取当前周 指定星期几 的日期（返回格式 yyyy-MM-dd）
     * whatDay 按照周一至周日时间参数规定：0-6，周一为0
     *
     * @param whatDay
     * @return
     */
    public static String getWeekWhatDayOfTime(int whatDay) {
        if (whatDay < 0 || whatDay > 6) {
            throw new IndexOutOfBoundsException("参数 [" + whatDay + "]不在周一至周日时间范围内");
        }
        SimpleDateFormat sdf = new SimpleDateFormat(YYYY_MM_DD); // 设置时间格式
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        // 判断要计算的日期是否是周日，如果是则减一天计算周六的，否则会出问题，计算到下一周去了
        int dayWeek = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
        if (1 == dayWeek) {
            cal.add(Calendar.DAY_OF_MONTH, -1);
        }
        cal.setFirstDayOfWeek(Calendar.MONDAY);// 设置一个星期的第一天，按中国的习惯一个星期的第一天是星期一
        int day = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
        cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);// 根据日历的规则，给当前日期减去星期几与一个星期第一天的差值
        cal.add(Calendar.DATE, whatDay);// 周一至周日时间是0-6
        return sdf.format(cal.getTime());
    }

    /**
     * 获取当前月的 第一天日期  （返回格式 yyyy-MM-dd）
     *
     * @return
     */
    public static String getMonthFirstDay() {
        SimpleDateFormat sdf = new SimpleDateFormat(YYYY_MM_DD); // 设置时间格式
        //获取当前月第一天：
        Calendar c = Calendar.getInstance();
        c.add(Calendar.MONTH, 0);
        c.set(Calendar.DAY_OF_MONTH, 1);//设置为1号,当前日期既为本月第一天
        return sdf.format(c.getTime());
    }

    /**
     * 返回 明天日期  （返回格式 yyyy-MM-dd）
     *
     * @return
     */
    public static String getNextOneday() {
        SimpleDateFormat sdf = new SimpleDateFormat(YYYY_MM_DD); // 设置时间格式
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.DAY_OF_YEAR, +1);
        return sdf.format(cal.getTime());
    }

    public static Date getTimeMonthsLater(int m) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, m);
        Timestamp date = Timestamp.valueOf(DATE_TIME_FORMAT.format(calendar.getTime()));
        return date;
    }
}